#include "header.h"
#include "ServerUtil.hpp"

using namespace std;

// 实现多路转接 select 的服务器
#define EMPTY -1

// 存储所有正在维护的 文件描述符
int fdarray[sizeof(fd_set) * 8] = {0};
int gnum = sizeof(fdarray) / sizeof(fdarray[0]);

void handleEvents(int lisSock, fd_set readfds);

int main()
{
    // 启动服务器
    int lisSock = ServerUtil::Getsock();
    ServerUtil::Bind(lisSock);
    ServerUtil::Listen(lisSock);

    cout << "开始监听" << endl;
    // 放到要维护的 fdsarray 里面, 并且第一个存放 监听套接字
    for (int i = 0; i < gnum; i ++)     fdarray[i] = EMPTY;
    fdarray[0] = lisSock;

    // 开始一轮一轮等待事件
    fd_set readfds;     // 只考虑读事件
    while (true)
    {
        int maxfd = -1;     // 记录最大的 fd 下标
        FD_ZERO(&readfds);  // 清空

        // 添加我们要关心的等待事件
        for (int i = 0; i < gnum; i ++)
        {
            if (fdarray[i] == EMPTY)
                continue;
            FD_SET(fdarray[i], &readfds);       // 添加要关心的写事件文件描述符
            maxfd = max(maxfd, fdarray[i]);     // 更新最大值
        }

        struct timeval maxtime = {100, 0};      // 秒和毫秒
        // 开始进行多路转接, maxfd 要关心的最大文件描述符
        int curcase = select(maxfd + 1, &readfds, nullptr, nullptr, &maxtime);
        cout << "有事件发生 [select return val] = " << curcase << endl;
        if (curcase == 0)
        {
            cerr << "等待超时" << endl;
            continue;
        }
        else if (curcase == -1)
        {
            cout << "等待出错" << endl;
            continue;
        }
        else    // 成功等待到事件
        {
            handleEvents(lisSock, readfds);
        }
    }

    return 0;
}


void handleEvents(int lisSock, fd_set readfds) 
{
    // 监听到了这些事件, 开始处理
    for (int i = 0; i < gnum; i ++)
    {
        if (fdarray[i] == EMPTY)                                // 如果是空的, 就直接跳过
            continue;
        if (i == 0 && fdarray[i] == lisSock)                    // 如果是监听套接字
        {
            if (FD_ISSET(lisSock, &readfds))                    // 如果读取事件中存在  监听套接字, 说明有人连接了
            {
                // 直接获取连接, 放在 fdarray 里面, 等待下次处理
                // 这里不应该马上处理, 客户端请求连接之后, 不一定会马上进行通信
                int client = ServerUtil::Accept(lisSock);
                cout << "新连接产生" << endl;
                int index = 0;                                  // 找到第一个闲置的下标
                for (; index < gnum; index ++)
                {
                    if (fdarray[index] == EMPTY)    break;
                }
                cout << "[index] = " << index << endl;          
                if (index == gnum)
                {
                    cerr << "1024 个位置已经被占满了, 无法处理" << endl;
                    continue;
                }
                fdarray[index] = client;                        // 可能准时发生读事件了
                cout << "有连接产生, 准备通信" << endl;
            }
        }
        else                                                    // 连接后的客户端的写事件
        {
            if (FD_ISSET(fdarray[i], &readfds))                 //  其他文件描述符有写事件, 直接读取
            {
                char readmsg[1024] = {0};
                int rd = read(fdarray[i], readmsg, 1023);
                if (rd > 0)
                {
                    readmsg[rd] = '\0';
                    cout << "[收到客户端的消息]: " << readmsg << endl;
                }
                else if (rd == 0)
                {
                    // 客户端关闭了写端
                    cout << "客户端关闭连接" << endl;
                    close(fdarray[i]);
                    fdarray[i] = EMPTY;
                }
            }
        }
    }
}